package com.foodorder.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * JWT工具类 - 安全增强版本
 * TODO: 后续升级到标准JJWT库
 */
@Component
public class JwtUtils {
    
    private static final Logger log = LoggerFactory.getLogger(JwtUtils.class);
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration}")
    private Long expiration;
    
    private final StringRedisTemplate redisTemplate;
    
    private static final String ALGORITHM = "HmacSHA256";
    private static final String BLACKLIST_PREFIX = "jwt:blacklist:";
    private static final SecureRandom secureRandom = new SecureRandom();
    
    public JwtUtils(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    /**
     * 生成JWT Token - 增强安全性
     */
    public String generateToken(Long userId, String email, String role) {
        try {
            // Header
            Map<String, Object> header = new HashMap<>();
            header.put("alg", "HS256");
            header.put("typ", "JWT");
            
            // Payload
            long now = System.currentTimeMillis() / 1000;
            long exp = now + (expiration / 1000);
            String jti = generateJti(); // 添加唯一标识符
            
            Map<String, Object> payload = new HashMap<>();
            payload.put("userId", userId);
            payload.put("email", email);
            payload.put("role", role);
            payload.put("iat", now);
            payload.put("exp", exp);
            payload.put("sub", email);
            payload.put("jti", jti); // JWT ID for blacklisting
            
            // 编码Header和Payload
            String encodedHeader = base64UrlEncode(mapToJson(header));
            String encodedPayload = base64UrlEncode(mapToJson(payload));
            
            // 生成签名 - 使用更强的密钥
            String signature = generateSignature(encodedHeader + "." + encodedPayload);
            
            return encodedHeader + "." + encodedPayload + "." + signature;
        } catch (Exception e) {
            log.error("生成JWT Token失败", e);
            throw new RuntimeException("Token生成失败");
        }
    }
    
    /**
     * 验证Token是否有效 - 增强安全验证
     */
    public boolean validateToken(String token) {
        try {
            // 基本格式检查
            String[] parts = token.split("\\.");
            if (parts.length != 3) {
                log.warn("JWT Token格式无效");
                return false;
            }
            
            // 检查Token是否在黑名单中
            if (isTokenBlacklisted(token)) {
                log.warn("Token已被列入黑名单");
                return false;
            }
            
            // 验证签名
            String expectedSignature = generateSignature(parts[0] + "." + parts[1]);
            if (!isSignatureValid(expectedSignature, parts[2])) {
                log.warn("JWT签名验证失败");
                return false;
            }
            
            // 验证过期时间
            Map<String, Object> payload = jsonToMap(base64UrlDecode(parts[1]));
            long exp = ((Number) payload.get("exp")).longValue();
            long now = System.currentTimeMillis() / 1000;
            
            if (now > exp) {
                log.warn("JWT Token已过期");
                return false;
            }
            
            return true;
        } catch (Exception e) {
            log.error("JWT Token验证失败", e);
            return false;
        }
    }
    
    /**
     * 从Token中获取用户ID
     */
    public Long getUserIdFromToken(String token) {
        try {
            Map<String, Object> payload = getPayloadFromToken(token);
            Object userId = payload.get("userId");
            if (userId instanceof Number) {
                return ((Number) userId).longValue();
            }
            return null;
        } catch (Exception e) {
            log.error("从Token获取用户ID失败", e);
            return null;
        }
    }
    
    /**
     * 从Token中获取邮箱
     */
    public String getEmailFromToken(String token) {
        try {
            Map<String, Object> payload = getPayloadFromToken(token);
            return (String) payload.get("email");
        } catch (Exception e) {
            log.error("从Token获取邮箱失败", e);
            return null;
        }
    }
    
    /**
     * 从Token中获取角色
     */
    public String getRoleFromToken(String token) {
        try {
            Map<String, Object> payload = getPayloadFromToken(token);
            return (String) payload.get("role");
        } catch (Exception e) {
            log.error("从Token获取角色失败", e);
            return null;
        }
    }
    
    /**
     * 获取Token过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        try {
            Map<String, Object> payload = getPayloadFromToken(token);
            long exp = ((Number) payload.get("exp")).longValue();
            return new Date(exp * 1000);
        } catch (Exception e) {
            log.error("获取Token过期时间失败", e);
            return null;
        }
    }
    
    /**
     * 刷新Token
     */
    public String refreshToken(String token) {
        try {
            Long userId = getUserIdFromToken(token);
            String email = getEmailFromToken(token);
            String role = getRoleFromToken(token);
            
            // 将旧Token加入黑名单
            blacklistToken(token);
            
            return generateToken(userId, email, role);
        } catch (Exception e) {
            log.error("刷新Token失败", e);
            throw new RuntimeException("Token刷新失败");
        }
    }
    
    /**
     * 将Token添加到黑名单
     */
    public void blacklistToken(String token) {
        try {
            Date expiration = getExpirationDateFromToken(token);
            if (expiration != null && redisTemplate != null) {
                long ttl = expiration.getTime() - System.currentTimeMillis();
                if (ttl > 0) {
                    String key = BLACKLIST_PREFIX + getJtiFromToken(token);
                    redisTemplate.opsForValue().set(key, "blacklisted", ttl, TimeUnit.MILLISECONDS);
                    log.info("Token已添加到黑名单");
                }
            }
        } catch (Exception e) {
            log.error("添加Token到黑名单失败: {}", e.getMessage());
        }
    }
    
    /**
     * 检查Token是否在黑名单中
     */
    public boolean isTokenBlacklisted(String token) {
        try {
            if (redisTemplate == null) {
                return false; // Redis不可用时允许通过
            }
            
            String jti = getJtiFromToken(token);
            if (jti != null) {
                String key = BLACKLIST_PREFIX + jti;
                return Boolean.TRUE.equals(redisTemplate.hasKey(key));
            }
            return false;
        } catch (Exception e) {
            log.error("检查Token黑名单状态失败: {}", e.getMessage());
            return false; // 出错时允许通过，避免影响正常业务
        }
    }
    
    /**
     * 从Token中获取JTI
     */
    private String getJtiFromToken(String token) {
        try {
            Map<String, Object> payload = getPayloadFromToken(token);
            return (String) payload.get("jti");
        } catch (Exception e) {
            return null;
        }
    }
    
    /**
     * 生成JWT ID
     */
    private String generateJti() {
        byte[] randomBytes = new byte[16];
        secureRandom.nextBytes(randomBytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes);
    }
    
    /**
     * 安全的签名比较
     */
    private boolean isSignatureValid(String expected, String actual) {
        if (expected == null || actual == null) {
            return false;
        }
        
        if (expected.length() != actual.length()) {
            return false;
        }
        
        // 使用时间常数比较避免时序攻击
        int result = 0;
        for (int i = 0; i < expected.length(); i++) {
            result |= expected.charAt(i) ^ actual.charAt(i);
        }
        return result == 0;
    }
    
    /**
     * 从Token中获取Payload
     */
    private Map<String, Object> getPayloadFromToken(String token) {
        String[] parts = token.split("\\.");
        if (parts.length != 3) {
            throw new RuntimeException("无效的JWT Token格式");
        }
        
        String payloadJson = base64UrlDecode(parts[1]);
        return jsonToMap(payloadJson);
    }
    
    /**
     * 生成签名
     */
    private String generateSignature(String data) throws NoSuchAlgorithmException, InvalidKeyException {
        Mac mac = Mac.getInstance(ALGORITHM);
        SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), ALGORITHM);
        mac.init(secretKeySpec);
        
        byte[] signatureBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return base64UrlEncode(signatureBytes);
    }
    
    /**
     * Base64 URL编码
     */
    private String base64UrlEncode(String input) {
        return base64UrlEncode(input.getBytes(StandardCharsets.UTF_8));
    }
    
    private String base64UrlEncode(byte[] input) {
        return Base64.getUrlEncoder().withoutPadding().encodeToString(input);
    }
    
    /**
     * Base64 URL解码
     */
    private String base64UrlDecode(String input) {
        byte[] decoded = Base64.getUrlDecoder().decode(input);
        return new String(decoded, StandardCharsets.UTF_8);
    }
    
    /**
     * Map转JSON（安全实现）
     */
    private String mapToJson(Map<String, Object> map) {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        
        boolean first = true;
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (!first) {
                sb.append(",");
            }
            first = false;
            
            sb.append("\"").append(escapeJson(entry.getKey())).append("\":");
            Object value = entry.getValue();
            if (value instanceof String) {
                sb.append("\"").append(escapeJson((String) value)).append("\"");
            } else {
                sb.append(value);
            }
        }
        
        sb.append("}");
        return sb.toString();
    }
    
    /**
     * JSON转Map（安全实现）
     */
    private Map<String, Object> jsonToMap(String json) {
        Map<String, Object> map = new HashMap<>();
        
        // 移除首尾大括号
        json = json.trim();
        if (json.startsWith("{")) {
            json = json.substring(1);
        }
        if (json.endsWith("}")) {
            json = json.substring(0, json.length() - 1);
        }
        
        if (json.isEmpty()) {
            return map;
        }
        
        // 分割键值对
        String[] pairs = json.split(",");
        for (String pair : pairs) {
            String[] keyValue = pair.split(":", 2);
            if (keyValue.length == 2) {
                String key = keyValue[0].trim().replaceAll("\"", "");
                String value = keyValue[1].trim();
                
                // 解析值
                if (value.startsWith("\"") && value.endsWith("\"")) {
                    // 字符串值
                    map.put(key, value.substring(1, value.length() - 1));
                } else if (value.matches("\\d+")) {
                    // 数字值
                    map.put(key, Long.parseLong(value));
                } else {
                    // 其他值
                    map.put(key, value);
                }
            }
        }
        
        return map;
    }
    
    /**
     * JSON字符串转义
     */
    private String escapeJson(String input) {
        if (input == null) {
            return "";
        }
        return input.replace("\\", "\\\\")
                   .replace("\"", "\\\"")
                   .replace("\n", "\\n")
                   .replace("\r", "\\r")
                   .replace("\t", "\\t");
    }
} 